home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Tools / MPW / fgrep 1.1 / kwset.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-02-19  |  16.3 KB  |  608 lines  |  [TEXT/MPS ]

  1. /* kwset.c - search for any of a set of keywords.
  2.    Copyright 1989 Free Software Foundation
  3.           Written August 1989 by Mike Haertel.
  4.  
  5.    This program is free software; you can redistribute it and/or modify
  6.    it under the terms of the GNU General Public License as published by
  7.    the Free Software Foundation; either version 1, or (at your option)
  8.    any later version.
  9.  
  10.    This program is distributed in the hope that it will be useful,
  11.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.    GNU General Public License for more details.
  14.  
  15.    You should have received a copy of the GNU General Public License
  16.    along with this program; if not, write to the Free Software
  17.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19.    The author may be reached (Email) at the address mike@ai.mit.edu,
  20.    or (US mail) as Mike Haertel c/o Free Software Foundation. */
  21.  
  22.  
  23. /* The algorithm implemented by these routines bears a startling resemblence
  24.    to one discovered by Beate Commentz-Walter, although it is not identical.
  25.    See "A String Matching Algorithm Fast on the Average," Technical Report,
  26.    IBM-Germany, Scientific Center Heidelberg, Tiergartenstrasse 15, D-6900
  27.    Heidelberg, Germany.  See also Aho, A.V., and M. Corasick, "Efficient
  28.    String Matching:  An Aid to Bibliographic Search," CACM June 1975,
  29.    Vol. 18, No. 6, which describes the failure function used below. */
  30.  
  31. #include <stdlib.h>
  32. #include <limits.h>
  33.  
  34. #include "kwset.h"
  35. #include "obstack.h"
  36.  
  37. #define NCHAR (UCHAR_MAX + 1)
  38. #define obstack_chunk_alloc malloc
  39. #define obstack_chunk_free free
  40.  
  41. /* Balanced tree of edges and labels leaving a given trie node. */
  42. struct tree
  43. {
  44.   struct tree *llink;        /* Left link; MUST be first field. */
  45.   struct tree *rlink;        /* Right link (to larger labels). */
  46.   struct trie *trie;        /* Trie node pointed to by this edge. */
  47.   unsigned char label;        /* Label on this edge. */
  48.   char balance;            /* Difference in depths of subtrees. */
  49. };
  50.  
  51. /* Node of a trie representing a set of reversed keywords. */
  52. struct trie
  53. {
  54.   unsigned int accepting;    /* Word index of accepted word, or zero. */
  55.   struct tree *links;        /* Tree of edges leaving this node. */
  56.   struct trie *parent;        /* Parent of this node. */
  57.   struct trie *next;        /* List of all trie nodes in level order. */
  58.   struct trie *fail;        /* Aho-Corasick failure function. */
  59.   int depth;            /* Depth of this node from the root. */
  60.   int shift;            /* Shift function for search failures. */
  61.   int maxshift;            /* Max shift of self and descendents. */
  62. };
  63.  
  64. /* Structure returned opaquely to the caller, containing everything. */
  65. struct kwset
  66. {
  67.   struct obstack obstack;    /* Obstack for node allocation. */
  68.   int words;            /* Number of words in the trie. */
  69.   struct trie *trie;        /* The trie itself. */
  70.   int mind;            /* Minimum depth of an accepting node. */
  71.   int maxd;            /* Maximum depth of any node. */
  72.   int delta[NCHAR];        /* Delta table for rapid search. */
  73.   struct trie *next[NCHAR];    /* Table of children of the root. */
  74.   const char *trans;        /* Character translation table. */
  75. };
  76.  
  77. /* Allocate and initialize a keyword set object, returning an opaque
  78.    pointer to it.  Return NULL if memory is not available. */
  79. kwset_t
  80. kwsalloc(const char *trans)
  81. {
  82.   struct kwset *kwset;
  83.  
  84.   kwset = (struct kwset *) malloc(sizeof (struct kwset));
  85.   if (!kwset)
  86.     return NULL;
  87.  
  88.   obstack_init(&kwset->obstack);
  89.   kwset->words = 0;
  90.   kwset->trie
  91.     = (struct trie *) obstack_alloc(&kwset->obstack, sizeof (struct trie));
  92.   if (!kwset->trie)
  93.     {
  94.       kwsfree((kwset_t) kwset);
  95.       return NULL;
  96.     }
  97.   kwset->trie->accepting = 0;
  98.   kwset->trie->links = NULL;
  99.   kwset->trie->parent = NULL;
  100.   kwset->trie->next = NULL;
  101.   kwset->trie->fail = NULL;
  102.   kwset->trie->depth = 0;
  103.   kwset->trie->shift = 0;
  104.   kwset->mind = INT_MAX;
  105.   kwset->maxd = -1;
  106.   kwset->trans = trans;
  107.  
  108.   return (kwset_t) kwset;
  109. }
  110.  
  111. /* Add the given string to the contents of the keyword set.  Return NULL
  112.    for success, an error message otherwise. */
  113. const char *
  114. kwsincr(kwset_t kws, const char *text, size_t len)
  115. {
  116.   struct kwset *kwset;
  117.   register struct trie *trie;
  118.   register unsigned char label;
  119.   register struct tree *link;
  120.   register int depth;
  121.   struct tree *links[12];
  122.   enum { L, R } dirs[12];
  123.   struct tree *t, *r, *l, *rl, *lr;
  124.  
  125.   kwset = (struct kwset *) kws;
  126.   trie = kwset->trie;
  127.   text += len;
  128.  
  129.   /* Descend the trie (built of reversed keywords) character-by-character,
  130.      installing new nodes when necessary. */
  131.   while (len--)
  132.     {
  133.       label = kwset->trans ? kwset->trans[(unsigned char) *--text] : *--text;
  134.  
  135.       /* Descend the tree of outgoing links for this trie node,
  136.      looking for the current character and keeping track
  137.      of the path followed. */
  138.       link = trie->links;
  139.       links[0] = (struct tree *) &trie->links;
  140.       dirs[0] = L;
  141.       depth = 1;
  142.  
  143.       while (link && label != link->label)
  144.     {
  145.       links[depth] = link;
  146.       if (label < link->label)
  147.         dirs[depth++] = L, link = link->llink;
  148.       else
  149.         dirs[depth++] = R, link = link->rlink;
  150.     }
  151.  
  152.       /* The current character doesn't have an outgoing link at
  153.      this trie node, so build a new trie node and install
  154.      a link in the current trie node's tree. */
  155.       if (!link)
  156.     {
  157.       link = (struct tree *) obstack_alloc(&kwset->obstack,
  158.                            sizeof (struct tree));
  159.       if (!link)
  160.         return "memory exhausted";
  161.       link->llink = NULL;
  162.       link->rlink = NULL;
  163.       link->trie = (struct trie *) obstack_alloc(&kwset->obstack,
  164.                              sizeof (struct trie));
  165.       if (!link->trie)
  166.         return "memory exhausted";
  167.       link->trie->accepting = 0;
  168.       link->trie->links = NULL;
  169.       link->trie->parent = trie;
  170.       link->trie->next = NULL;
  171.       link->trie->fail = NULL;
  172.       link->trie->depth = trie->depth + 1;
  173.       link->trie->shift = 0;
  174.       link->label = label;
  175.       link->balance = 0;
  176.  
  177.       /* Install the new tree node in its parent. */
  178.       if (dirs[--depth] == L)
  179.         links[depth]->llink = link;
  180.       else
  181.         links[depth]->rlink = link;
  182.  
  183.       /* Back up the tree fixing the balance flags. */
  184.       while (depth && !links[depth]->balance)
  185.         {
  186.           if (dirs[depth] == L)
  187.         --links[depth]->balance;
  188.           else
  189.         ++links[depth]->balance;
  190.           --depth;
  191.         }
  192.  
  193.       /* Rebalance the tree by pointer rotations if necessary. */
  194.       if (depth && (dirs[depth] == L && --links[depth]->balance
  195.             || dirs[depth] == R && ++links[depth]->balance))
  196.         {
  197.           switch (links[depth]->balance)
  198.         {
  199.         case (char) -2:
  200.           switch (dirs[depth + 1])
  201.             {
  202.             case L:
  203.               r = links[depth], t = r->llink, rl = t->rlink;
  204.               t->rlink = r, r->llink = rl;
  205.               t->balance = r->balance = 0;
  206.               break;
  207.             case R:
  208.               r = links[depth], l = r->llink, t = l->rlink;
  209.               rl = t->rlink, lr = t->llink;
  210.               t->llink = l, l->rlink = lr, t->rlink = r, r->llink = rl;
  211.               l->balance = t->balance != 1 ? 0 : -1;
  212.               r->balance = t->balance != (char) -1 ? 0 : 1;
  213.               t->balance = 0;
  214.               break;
  215.             }
  216.           break;
  217.         case 2:
  218.           switch (dirs[depth + 1])
  219.             {
  220.             case R:
  221.               l = links[depth], t = l->rlink, lr = t->llink;
  222.               t->llink = l, l->rlink = lr;
  223.               t->balance = l->balance = 0;
  224.               break;
  225.             case L:
  226.               l = links[depth], r = l->rlink, t = r->llink;
  227.               lr = t->llink, rl = t->rlink;
  228.               t->llink = l, l->rlink = lr, t->rlink = r, r->llink = rl;
  229.               l->balance = t->balance != 1 ? 0 : -1;
  230.               r->balance = t->balance != (char) -1 ? 0 : 1;
  231.               t->balance = 0;
  232.               break;
  233.             }
  234.           break;
  235.         }
  236.  
  237.           if (dirs[depth - 1] == L)
  238.         links[depth - 1]->llink = t;
  239.           else
  240.         links[depth - 1]->rlink = t;
  241.         }
  242.     }
  243.  
  244.       trie = link->trie;
  245.     }
  246.  
  247.   /* Mark the node we finally reached as accepting, encoding the
  248.      index number of this word in the keyword set so far. */
  249.   if (!trie->accepting)
  250.     trie->accepting = 1 + 2 * kwset->words;
  251.   ++kwset->words;
  252.  
  253.   /* Keep track of the longest and shortest string of the keyword set. */
  254.   if (trie->depth < kwset->mind)
  255.     kwset->mind = trie->depth;
  256.   if (trie->depth > kwset->maxd)
  257.     kwset->maxd = trie->depth;
  258.  
  259.   return NULL;
  260. }
  261.  
  262. /* Enqueue the trie nodes referenced from the given tree in the
  263.    given queue. */
  264. static void
  265. enqueue(struct tree *tree, struct trie **last)
  266. {
  267.   if (!tree)
  268.     return;
  269.   enqueue(tree->llink, last);
  270.   enqueue(tree->rlink, last);
  271.   (*last) = (*last)->next = tree->trie;
  272. }
  273.  
  274. /* Compute the Aho-Corasick failure function for the trie nodes referenced
  275.    from the given tree, given the failure function for their parent as
  276.    well as a last resort failure node. */
  277.    
  278. static void
  279. treefails(register struct tree *tree, struct trie *fail, struct trie *recourse)
  280. {
  281.   register struct tree *link;
  282.  
  283.   if (!tree)
  284.     return;
  285.  
  286.   treefails(tree->llink, fail, recourse);
  287.   treefails(tree->rlink, fail, recourse);
  288.  
  289.   /* Find, in the chain of fails going back to the root, the first
  290.      node that has a descendent on the current label. */
  291.   while (fail)
  292.     {
  293.       link = fail->links;
  294.       while (link && tree->label != link->label)
  295.     if (tree->label < link->label)
  296.       link = link->llink;
  297.     else
  298.       link = link->rlink;
  299.       if (link)
  300.     {
  301.       tree->trie->fail = link->trie;
  302.       return;
  303.     }
  304.       fail = fail->fail;
  305.     }
  306.  
  307.   tree->trie->fail = recourse;
  308. }
  309.  
  310. /* Set delta entries for the links of the given tree such that
  311.    the preexisting delta value is larger than the current depth. */
  312. static void
  313. treedelta(register struct tree *tree, register int depth, int delta[])
  314. {
  315.   if (!tree)
  316.     return;
  317.   treedelta(tree->llink, depth, delta);
  318.   treedelta(tree->rlink, depth, delta);
  319.   if (depth < delta[tree->label])
  320.     delta[tree->label] = depth;
  321. }
  322.  
  323. /* Return true if A has every label in B. */
  324. static int
  325. hasevery(register struct tree *a, register struct tree *b)
  326. {
  327.   if (!b)
  328.     return 1;
  329.   if (!hasevery(a, b->llink))
  330.     return 0;
  331.   if (!hasevery(a, b->rlink))
  332.     return 0;
  333.   while (a && b->label != a->label)
  334.     if (b->label < a->label)
  335.       a = a->llink;
  336.     else
  337.       a = a->rlink;
  338.   return !!a;
  339. }
  340.  
  341. /* Compute a vector, indexed by character code, of the trie nodes
  342.    referenced from the given tree. */
  343. static void
  344. treenext(struct tree *tree, struct trie *next[])
  345. {
  346.   if (!tree)
  347.     return;
  348.   treenext(tree->llink, next);
  349.   treenext(tree->rlink, next);
  350.   next[tree->label] = tree->trie;
  351. }
  352.  
  353. /* Compute the shift for each trie node, as well as the delta
  354.    table and next cache for the given keyword set. */
  355. const char *
  356. kwsprep(kwset_t kws)
  357. {
  358.   register struct kwset *kwset;
  359.   register int i;
  360.   register struct trie *curr, *fail;
  361.   register const char *trans;
  362.   int delta[NCHAR];
  363.   struct trie *last, *next[NCHAR];
  364.  
  365.   kwset = (struct kwset *) kws;
  366.  
  367.   /* Initial values for the delta table; will be changed later.  The
  368.      delta entry for a given character is the smallest depth of any
  369.      node at which an outgoing edge is labeled by that character. */
  370.   for (i = 0; i < NCHAR; ++i)
  371.     delta[i] = kwset->mind;
  372.  
  373.   /* Traverse the nodes of the trie in level order, simultaneously
  374.      computing the delta table, failure function, and shift function. */
  375.   for (curr = last = kwset->trie; curr; curr = curr->next)
  376.     {
  377.       /* Enqueue the immediate descendents in the level order queue. */
  378.       enqueue(curr->links, &last);
  379.  
  380.       curr->shift = kwset->mind;
  381.       curr->maxshift = kwset->mind;
  382.  
  383.       /* Update the delta table for the descendents of this node. */
  384.       treedelta(curr->links, curr->depth, delta);
  385.  
  386.       /* Compute the failure function for the decendents of this node. */
  387.       treefails(curr->links, curr->fail, kwset->trie);
  388.  
  389.       /* Update the shifts at each node in the current node's chain
  390.      of fails back to the root. */
  391.       for (fail = curr->fail; fail; fail = fail->fail)
  392.     {
  393.       /* If the current node has some outgoing edge that the fail
  394.          doesn't, then the shift at the fail should be no larger
  395.          than the difference of their depths. */
  396.       if (!hasevery(fail->links, curr->links))
  397.         if (curr->depth - fail->depth < fail->shift)
  398.           fail->shift = curr->depth - fail->depth;
  399.  
  400.       /* If the current node is accepting then the shift at the
  401.          fail and its descendents should be no larger than the
  402.          difference of their depths. */
  403.       if (curr->accepting && fail->maxshift > curr->depth - fail->depth)
  404.         fail->maxshift = curr->depth - fail->depth;
  405.     }
  406.     }
  407.  
  408.   /* Traverse the trie in level order again, fixing up all nodes whose
  409.      shift exceeds their inherited maxshift. */
  410.   for (curr = kwset->trie->next; curr; curr = curr->next)
  411.     {
  412.       if (curr->maxshift > curr->parent->maxshift)
  413.     curr->maxshift = curr->parent->maxshift;
  414.       if (curr->shift > curr->maxshift)
  415.     curr->shift = curr->maxshift;
  416.     }
  417.  
  418.   /* Create a vector, indexed by character code, of the outgoing links
  419.      from the root node. */
  420.   for (i = 0; i < NCHAR; ++i)
  421.     next[i] = NULL;
  422.   treenext(kwset->trie->links, next);
  423.  
  424.   /* Fix things up for any translation table. */
  425.   if (trans = kwset->trans)
  426.     for (i = 0; i < NCHAR; ++i)
  427.       {
  428.     kwset->delta[i] = delta[(unsigned char) trans[i]];
  429.     kwset->next[i] = next[(unsigned char) trans[i]];
  430.       }
  431.   else
  432.     for (i = 0; i < NCHAR; ++i)
  433.       {
  434.     kwset->delta[i] = delta[i];
  435.     kwset->next[i] = next[i];
  436.       }
  437.  
  438.   return NULL;
  439. }
  440.  
  441. /* Search through the given text for a match of any member of the
  442.    given keyword set.  Return a pointer to the first character of
  443.    the matching substring, or NULL if no match is found.  If FOUNDLEN
  444.    is non-NULL store in the referenced location the length of the
  445.    matching substring.  Similarly, if FOUNDIDX is non-NULL, store
  446.    in the referenced location the index number of the particular
  447.    keyword matched. */
  448. char *
  449. kwsexec(kwset_t kws, char *text, size_t len, struct kwsmatch *kwsmatch)
  450. {
  451.   struct kwset *kwset;
  452.   struct trie **next, *trie, *accept;
  453.   char *beg, *lim, *mch, *lmch;
  454.   register unsigned char c;
  455.   register int *delta, d;
  456.   register char *end, *qlim;
  457.   register struct tree *tree;
  458.   register const char *trans;
  459.  
  460.   /* Initialize register copies and look for easy ways out. */
  461.   kwset = (struct kwset *) kws;
  462.   if (len < kwset->mind)
  463.     return NULL;
  464.   next = kwset->next;
  465.   delta = kwset->delta;
  466.   trans = kwset->trans;
  467.   lim = text + len;
  468.   end = text;
  469.   if (d = kwset->mind)
  470.     mch = NULL;
  471.   else
  472.     {
  473.       mch = text, accept = kwset->trie;
  474.       goto match;
  475.     }
  476.  
  477.   if (len >= 4 * kwset->mind)
  478.     qlim = lim - 4 * kwset->mind;
  479.   else
  480.     qlim = NULL;
  481.  
  482.   while (lim - end >= d)
  483.     {
  484.       if (qlim && end <= qlim)
  485.     {
  486.       end += d - 1;
  487.       while ((d = delta[c = *end]) && end < qlim)
  488.         {
  489.           end += d;
  490.           end += delta[(unsigned char) *end];
  491.           end += delta[(unsigned char) *end];
  492.         }
  493.       ++end;
  494.     }
  495.       else
  496.     d = delta[c = (end += d)[-1]];
  497.       if (d)
  498.     continue;
  499.       beg = end - 1;
  500.       trie = next[c];
  501.       if (trie->accepting)
  502.     {
  503.       mch = beg;
  504.       accept = trie;
  505.     }
  506.       d = trie->shift;
  507.       while (beg > text)
  508.     {
  509.       c = trans ? trans[(unsigned char) *--beg] : *--beg;
  510.       tree = trie->links;
  511.       while (tree && c != tree->label)
  512.         if (c < tree->label)
  513.           tree = tree->llink;
  514.         else
  515.           tree = tree->rlink;
  516.       if (tree)
  517.         {
  518.           trie = tree->trie;
  519.           if (trie->accepting)
  520.         {
  521.           mch = beg;
  522.           accept = trie;
  523.         }
  524.         }
  525.       else
  526.         break;
  527.       d = trie->shift;
  528.     }
  529.       if (mch)
  530.     goto match;
  531.     }
  532.   return NULL;
  533.  
  534.  match:
  535.   /* Given a known match, find the longest possible match anchored
  536.      at or before its starting point.  This is nearly a verbatim
  537.      copy of the preceding main search loops. */
  538.   if (lim - mch > kwset->maxd)
  539.     lim = mch + kwset->maxd;
  540.   lmch = NULL;
  541.   d = 1;
  542.   while (lim - end >= d)
  543.     {
  544.       if (d = delta[c = (end += d)[-1]])
  545.     continue;
  546.       beg = end - 1;
  547.       if (!(trie = next[c]))
  548.     {
  549.       d = 1;
  550.       continue;
  551.     }
  552.       if (trie->accepting && beg <= mch)
  553.     {
  554.       lmch = beg;
  555.       accept = trie;
  556.     }
  557.       d = trie->shift;
  558.       while (beg > text)
  559.     {
  560.       c = trans ? trans[(unsigned char) *--beg] : *--beg;
  561.       tree = trie->links;
  562.       while (tree && c != tree->label)
  563.         if (c < tree->label)
  564.           tree = tree->llink;
  565.         else
  566.           tree = tree->rlink;
  567.       if (tree)
  568.         {
  569.           trie = tree->trie;
  570.           if (trie->accepting && beg <= mch)
  571.         {
  572.           lmch = beg;
  573.           accept = trie;
  574.         }
  575.         }
  576.       else
  577.         break;
  578.       d = trie->shift;
  579.     }
  580.       if (lmch)
  581.     {
  582.       mch = lmch;
  583.       goto match;
  584.     }
  585.       if (!d)
  586.     d = 1;
  587.     }
  588.  
  589.   if (kwsmatch)
  590.     {
  591.       kwsmatch->index = accept->accepting / 2;
  592.       kwsmatch->beg[0] = mch;
  593.       kwsmatch->size[0] = accept->depth;
  594.     }
  595.   return mch;
  596. }
  597.   
  598. /* Free the components of the given keyword set. */
  599. void
  600. kwsfree(kwset_t kws)
  601. {
  602.   struct kwset *kwset;
  603.  
  604.   kwset = (struct kwset *) kws;
  605.   obstack_free(&kwset->obstack, (void *) NULL);
  606.   free((void *) kws);
  607. }
  608.